#include "HerzeleidPrecompiled.hxx"
#include "HerzeleidBasicAudioEngine.hxx"
#include "HerzeleidAudioStreamer.hxx"
using namespace HerzeleidMediaNamespaceImpl;

Void AudioEngineBase::Initialize()
{
	if ( m_bEngineInitialized )
	{
		ReleaseResources();
	}

	GetDefaultReverbConfigurationForSmallRoom( 
		&m_ReverbParameteresSmallRoom 
		);
	GetDefaultReverbConfigurationForLargeRoom( 
		&m_ReverbParameteresLargeRoom 
		);

	m_bAudioStarted = false;
	m_bEngineInitialized = true;
	m_SoundEffectEngine = nullptr;
	m_SoundEffectMasteringVoice = nullptr;
}

HerzeleidMediaNamespaceBegin class MediaEngineNotify : 
	public WRL::RuntimeClass < WRL::RuntimeClassFlags < WRL::ClassicCom >, 
		IMFMediaEngineNotify >
{
public:
	STDMETHODIMP EventNotify( 
		_In_ DWORD, // dw event
		_In_ DWORD_PTR, // param 1
		_In_ DWORD // param 2
		)
	{
		return( S_OK );
	}

} HerzeleidMediaNamespaceEnd;

Void AudioEngineBase::CreateResources()
{
	try
	{
		DirectX::ThrowIfFailed(
			XAudio2Create(&m_SoundEffectEngine)
			);

		// Configure engine callbacks 
		m_Callbacks.Initialize( 
			this 
			);
		m_SoundEffectEngine->RegisterForCallbacks( 
			&m_Callbacks 
			);

		DirectX::ThrowIfFailed(
			m_SoundEffectEngine->CreateMasteringVoice( 
				(&m_SoundEffectMasteringVoice), // outptr mastering voice
				DefaultNumInputChannels, // input channels (default 2)
				DefaultSampleRate, // sample rate (default 48000)
				NoFlags, // no additional falgs 
				nullptr, // use default device
				nullptr, // no effect chain 
				AudioCategory_GameEffects
				)
			);

		CreateReverb( 
			&m_ReverbParameteresSmallRoom, 
			&m_SoundEffectReverbVoiceSmallRoom, 
			true 
			);

		CreateReverb( 
			&m_ReverbParameteresLargeRoom, 
			&m_SoundEffectReverbVoiceLargeRoom, 
			true 
			);
	}
	catch( ... )
	{
		SetExperiencedCriticalError();
	}

	try
	{
        WRL::ComPtr<MediaEngineNotify> Notify = WRL::Make<MediaEngineNotify>();
		WRL::ComPtr<IMFAttributes> MediaEngineAttributes;
        WRL::ComPtr<IUnknown> UnknownNotify;
		
		DirectX::ThrowIfFailed(
			Notify.As( &UnknownNotify )
			);

        DirectX::ThrowIfFailed(
            MediaEngineAttributes->SetUnknown(
				MF_MEDIA_ENGINE_CALLBACK, 
				UnknownNotify.Get()
				)
            );

        DirectX::ThrowIfFailed(
            MediaEngineAttributes->SetUnknown(
				MF_MEDIA_ENGINE_CALLBACK, 
				UnknownNotify.Get()
				)
            );

		m_MediaEngine = nullptr;

		WRL::ComPtr<IMFMediaEngineClassFactory> MediaEngineFactory;

		DirectX::ThrowIfFailed(
			CoCreateInstance( 
				CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER,
				IID_PPV_ARGS(MediaEngineFactory.GetAddressOf())
				)
			);

		DirectX::ThrowIfFailed(
			MediaEngineFactory->CreateInstance(
				0, MediaEngineAttributes.Get(), 
				m_MediaEngine.GetAddressOf()
				)
			);
	}
	catch( ... )
	{
		m_MediaEngine = nullptr;
	}
}


Void AudioEngineBase::CreateReverb(
	_In_ AudioEffectReverbDescPtr pParams,
	_Inout_ SubmixVoiceAddress ppSubmix,
	_In_ Bool bEnableEffect
	)
{
	XAUDIO2_EFFECT_DESCRIPTOR SoundEffectDesc;
	XAUDIO2_EFFECT_CHAIN SoundEffectChain;
	WRL::ComPtr<IUnknown> SoundEffectXapo;

	DirectX::ThrowIfFailed(
		XAudio2CreateReverb( 
			SoundEffectXapo.GetAddressOf() 
			)
		);

    SoundEffectDesc.pEffect = SoundEffectXapo.Get();
    SoundEffectDesc.InitialState = false;
    SoundEffectDesc.OutputChannels = 2;
    SoundEffectChain.pEffectDescriptors = &SoundEffectDesc;
    SoundEffectChain.EffectCount = 1;

	DirectX::ThrowIfFailed(
		m_SoundEffectEngine->CreateSubmixVoice( 
			ppSubmix, DefaultNumInputChannels, DefaultSampleRate, 
			NoFlags, 0, nullptr, &SoundEffectChain 
			)
		);

	DirectX::ThrowIfFailed(
		(*ppSubmix)->SetEffectParameters(
			0, pParams, sizeof(AudioEffectReverbDesc)
			)
		);

	DirectX::ThrowIfFailed(
		(*ppSubmix)->SetVolume( 1.0f )
		);

	if ( bEnableEffect )
	{
		DirectX::ThrowIfFailed(
			(*ppSubmix)->EnableEffect( 0u )
			);
	}

	Float32 faOutputMatrix[] = { 0.0f, 0.0f, 0.0f, 0.0f };
	DirectX::ThrowIfFailed(
		(*ppSubmix)->SetOutputMatrix( 
			m_SoundEffectMasteringVoice, 
			DefaultNumInputChannels,	// 2
			DefaultNumOutputChannels,	// 2
			faOutputMatrix				// 0, 0, 0, 0
			)
		);
}

Void AudioEngineBase::ReleaseResources()
{
	if ( m_SoundEffectReverbVoiceSmallRoom )
	{
		m_SoundEffectReverbVoiceSmallRoom->DestroyVoice();
		m_SoundEffectReverbVoiceSmallRoom = nullptr;
	}

	if ( m_SoundEffectReverbVoiceLargeRoom )
	{
		m_SoundEffectReverbVoiceLargeRoom->DestroyVoice();
		m_SoundEffectReverbVoiceLargeRoom = nullptr;
	}

	if ( m_SoundEffectMasteringVoice )
	{
		m_SoundEffectMasteringVoice->DestroyVoice();
		m_SoundEffectMasteringVoice = nullptr;
	}

	m_SoundEffectEngine = nullptr;
	m_MediaEngine = nullptr;

}

Void AudioEngineBase::ReleaseSourceVoice( 
	_Inout_ BasicSoundEffectPtr pEffect
	)
{
	Safe_DeleteArray( pEffect->SoundEffectBufferData );
	pEffect->SoundEffectSourceVoice->DestroyVoice();
	pEffect->SoundEffectSourceVoice = nullptr;
}

Void AudioEngineBase::CreateSourceVoice( 
	_Inout_ BasicSoundEffectPtr pEffect,
	_In_ String16ConstRef wszWav
	)
{
	AudioStreamer SoundEffectStream;
	SoundEffectStream.Initialize(
		wszWav.data()
		);

	auto uBufferLength = SoundEffectStream.MaxStreamLength();
	pEffect->SoundEffectBufferData = new Byte[uBufferLength];
	Standard::ZeroValueArray( 
		_In_ (pEffect->SoundEffectBufferData), 
		_In_ uBufferLength 
		);

	SoundEffectStream.ReadAllMemory(
		_In_ pEffect->SoundEffectBufferData, _In_ uBufferLength,
		_Out_ (&pEffect->SoundEffectBufferLength)
		);

	XAUDIO2_SEND_DESCRIPTOR Descs[3];
	Standard::ZeroValueArray( _In_ Descs, _In_ ARRAYSIZE(Descs) );
	Descs[0].pOutputVoice = m_SoundEffectMasteringVoice;
	Descs[1].pOutputVoice = m_SoundEffectReverbVoiceSmallRoom;
	Descs[2].pOutputVoice = m_SoundEffectReverbVoiceLargeRoom;

	XAUDIO2_VOICE_SENDS Sends = { 0 };
	Sends.SendCount = ARRAYSIZE( Descs );
	Sends.pSends = Descs;

	DirectX::ThrowIfFailed(
		m_SoundEffectEngine->CreateSourceVoice(
			&pEffect->SoundEffectSourceVoice,
			SoundEffectStream.WaveFormat(),
			0, 0.1f, nullptr,
			&Sends
			)
		);

	pEffect->SoundEffectSampleRate = SoundEffectStream.WaveFormat()->nSamplesPerSec;
	Standard::ZeroValue( &pEffect->SoundEffectBufferDesc );

	pEffect->SoundEffectBufferDesc.AudioBytes = pEffect->SoundEffectBufferLength;
	pEffect->SoundEffectBufferDesc.pAudioData = pEffect->SoundEffectBufferData;
	pEffect->SoundEffectBufferDesc.Flags = XAUDIO2_END_OF_STREAM;
	pEffect->SoundEffectBufferDesc.pContext = pEffect;
	pEffect->SoundEffectBufferDesc.LoopCount = 0; // XAUDIO2_LOOP_INFINITE

	DirectX::ThrowIfFailed(
		pEffect->SoundEffectSourceVoice->SubmitSourceBuffer(
			&pEffect->SoundEffectBufferDesc
			)
		);
}